/*
This file is part of cdbnlib.

    cdbnlib is free software: you can redistribute it and/or modify it under
    the terms of the GNU Lesser General Public License as published by the Free
    Software Foundation, either version 3 of the License, or (at your option)
    any later version.

    cdbnlib is distributed in the hope that it will be useful, but WITHOUT ANY
    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
    more details.

    You should have received a copy of the GNU Lesser General Public License
    along with cdbnlib.  If not, see <http://www.gnu.org/licenses/>.
*/


#ifndef DBNLIB_H
#define DBNLIB_H

/* This structure holds a row of neurons
 * with their associated biases and
 * activation probabilities
 */
typedef struct dl_layer_struct {
	int count; // number of neurons
	double *bias; // biases
	double *prob; // probabilities
	char *sample; // stochastic binary sample
} dl_layer_t;


/* This structure holds a Restricted
 * Boltzmann machine
 */
typedef struct dl_rbm_struct {
	// Here are the main variables for normal
	// usage:
	double *weight; // the weights
	int weight_count; // the number of weights
	dl_layer_t *vis; // ptr to visible layer
	dl_layer_t *hid; // ptr to hidden layer

	// Down here are the pointers to working
	// memory to be used in the learning
	// process (internal use only):
	double *posprods; // positive phase accumulators
	double *negprods; // negative phase accumulators
	double *poshidact;
	double *posvisact;
	double *neghidact;
	double *negvisact;
	double accerr;
	double *weight_inc;
	double *visbiasinc;
	double *hidbiasinc;
} dl_rbm_t;


/* This structure holds an associative
 * layer Restricted Boltzmann machine
 */
typedef struct dl_rbm_assoc_struct {
	int n; // number channels to associate
	dl_rbm_t **rbm; // the RBMs underneath (n)
	dl_layer_t **vis; // list of ptr to vis layers (n)
	dl_layer_t *hid; // the associative hid layer
} dl_rbm_assoc_t;

/* This function implements the polar form of the Box-Muller
 * transformation for generating Gaussian pseudo-random numbers
 * given a source of uniform pseudo-random numbers
 *
 * source:
 * http://www.taygeta.com/random/gaussian.html
 */
void dl_get_box_muller_random_normal_2d(double *randn1, double *randn2, double scale);

/* This function creates a new layer of neurons
 */
void dl_create_layer(dl_layer_t *layer, int count);


/* This function creates a new Restricted Boltzmann Machine
 */
void dl_create_rbm(dl_rbm_t *rbm, dl_layer_t *vis, dl_layer_t *hid);


/* This function creates a new associative layer in a
 * Restricted Boltzmann Machine
 */
void dl_create_rbm_assoc(dl_rbm_assoc_t *rbm, dl_layer_t **vis_list, int n, dl_layer_t *hid);

/* This function initializes the biases and probs
 * of a layer
 */
void dl_init_layer(dl_layer_t *layer);

/* This function initializes the weights an RBM
 */
void dl_init_rbm(dl_rbm_t *rbm);

/* Frees the memory of allocated by the structure
 */
void dl_del_layer(dl_layer_t *layer);
void dl_del_rbm(dl_rbm_t *rbm);
void dl_del_rbm_assoc(dl_rbm_assoc_t *rbm);

/* Reset all the positive and negative phase learning
 * accumulators
 */
void dl_rbm_reset_all_accumulators(dl_rbm_t *rbm);

/* Reset the positive and negative phase learning
 * accumulators to begin another epoch
 */
void dl_rbm_reset_epoch_accumulators(dl_rbm_t *rbm);

/* This function gets a sample from the given layer
 */
void dl_layer_get_sample(dl_layer_t *layer);

/* This function gets a sample from the given layer
 * according to the probabilities passed as argument
 */
void dl_layer_get_sample_with_prob(dl_layer_t *layer, double *prob);

/* This function updates the probabilities in the
 * hidden layer
 */
void dl_rbm_update_hid(dl_rbm_t *rbm);
void dl_rbm_assoc_update_hid(dl_rbm_assoc_t *rbm);
void dl_rbm_update_hid_from_sample(dl_rbm_t *rbm, char *sample);
void dl_rbm_assoc_update_hid_from_sample(dl_rbm_assoc_t *rbm, char **sample);


/* This function updates the probabilities in the
 * visible layer
 */
void dl_rbm_update_vis(dl_rbm_t *rbm);
void dl_rbm_assoc_update_vis(dl_rbm_assoc_t *rbm);
void dl_rbm_update_vis_from_sample(dl_rbm_t *rbm, char *sample);
void dl_rbm_assoc_update_vis_from_sample(dl_rbm_assoc_t *rbm, char **sample);


/* This function trains the RBM using contrastive divergence
 */
void dl_rbm_train(dl_rbm_t *rbm, double *input_prob, double learning_rate,
			double momentum, char reset_all, char reset_epoch);
void dl_rbm_assoc_train(dl_rbm_assoc_t *rbm, double **input_prob_list, int n,
	double learning_rate,double momentum, char reset_all, char reset_epoch);

#endif
